﻿#pragma once


#include <thread>
#include <vulkan/vulkan.hpp>

#include "Containers/Vector.h"
#include "Containers/ThreadSafePtr.h"

#include "Render/VulkanDescriptor/VulkanDescriptorLayout.h"
#include "Render/VulkanDescriptor/VulkanDescriptorPoolSize.h"
#include "Render/VulkanDescriptor/VulkanDescriptorPool.h"

#include "Math/Matrix4x4.h"
#include "Math/Color.h"

#include "Render/Widget/RectWidgetPipeline.h"
#include "Render/Widget/WidgetPipelines.h"
#include "Render/Widget/TextWidgetPipeline.h"
#include "Render/ForegroundMesh/ForegroundMeshPipeline.h"
#include "Render/Scene/SceneGlobal.h"
#include "Render/VulkanMemory/MemoryPool.h"
#include "Render/VulkanRecycleObjects.h"

class CVulkanWindow;
class CVulkanOnlyShaders;

class CPreRenderMaterial;
class CAllPreRenderData;

class CVulkanTexture2Ds;
class CVulkanTextureCubes;
class CVulkanShaders;
class CVulkanMaterials;
class CVulkanMeshs;

class CVulkanTexture2D;
class CVulkanTextureCube;
class CVulkanMesh;
class CVulkanShader;
class CVulkanMaterial;

class CTexture2DPixels;
class CWriteInTextureCube;
class CWriteInMesh;
class CWriteInShader;

//最终的渲染线程
class CVulkanContext
{
public:
    static CVulkanContext* Get();
    ~CVulkanContext();

    void Initialize();

    //当前绘制到第几帧
    uint64_t CurrentFrame=0;
    //当前绘制器索引，0或1，由于上一帧绘制器的数据可能还在渲染
    //所以准备两个绘制器，交替使用
    uint8_t CurrentBufferIndex=0;

    void GotoNextFrame()
    {
        CurrentFrame++;
        CurrentBufferIndex=1-CurrentBufferIndex;
    }

    vk::PhysicalDevice& GetPhysicalDevice()
    {
        return PhysicalDevice;
    }

    vk::Instance& GetVulkanInstance()
    {
        return VkInstance;
    }

    vk::PhysicalDeviceMemoryProperties& GetDeviceMemoryProperties()
    {
        return DeviceMemoryProperties;
    }

    static bool IsDepthFormat(vk::Format Format);

    CVulkanShaders* GetShaderManager()
    {
        return ShadersMangers.get();
    }
private:   

    // the max image count ,
    // might less than it becuase limitation of surface 
    //const int MaxSwapchainImageCount=2;

    vk::Instance VkInstance;
    vk::PhysicalDevice  PhysicalDevice;
    vk::PhysicalDeviceMemoryProperties DeviceMemoryProperties;


    void SelectPhysicalDevice();


public:
    vk::Device GetDevice()
    {
        return Device;
    }

    uint32_t GetQueueFamilyIndex()
    {
        return QueueFamilyIndex;
    }

    vk::Queue GetPresentQueue()
    {
        return PresentQueue;
    }

    vk::Queue GetGraphicQueue()
    {
        return GraphicQueue;
    }

    uint32_t GetMemoryTypeIndex(vk::MemoryRequirements Requirment,vk::MemoryPropertyFlags RequiredFlag);

    vk::Format GetSwapChainImageFormat();

    vk::Format GetWidgetDepthImageFormat();

private:
    //propertied initialized by first window
    vk::Device Device;
    uint32_t QueueFamilyIndex=UINT32_MAX;
    vk::Queue GraphicQueue; //also present queue
    vk::Queue PresentQueue; //also present queue

    void SelectQueue();
    void CreateDevice();

    vk::Format SwapChainImageFormat= vk::Format::eB8G8R8A8Unorm;

    //widget render pass depth image format
    vk::Format WidgetDepthImageFormat= vk::Format::eD16Unorm; 
    

// widget render thread things:
public:
    vk::CommandPool GetRenderThreadCommandPool()
    {
        return RenderThreadCommandPool;
    }

    CVulkanAutoResetDescriptorPools* GetAutoResetDescriptorPool()
    {
        return AutoResetDescriptorPool+CurrentBufferIndex;
    }


    vk::RenderPass GetWidgetRenderPass();

    vk::Fence GetConvertImageLayoutFence();
    vk::CommandBuffer GetConvertImageLayoutCommandBuffer();

    //用于复制图像的命令缓冲区
    vk::Fence GetCopyImageFence();
    vk::CommandBuffer GetCopyImageCommandBuffer();

    SWidgetPipelines* GetWidgetPipelines();

    STextWidgetPipeline* GetTextWidgetPipeline();

    //for viewport widget
    SForegroundMeshPipeline* GetForegroundMeshPipeline();

    //for single color rect widget
    SRectWidgetPipeline* GetRectWidgetPipeline();
    
    struct SRectVertexPushConstant
    {
        static int GetSizeWithoutPadding()
        {
            return sizeof(float)*6;
        }

        //rgba;
        float Color[4];
        float ZOrder;

        //(0,1) for paint border,
        // border conner have a outter radius=1 and inner radius
        float InnerRadius=0.f;

        //shader will auto padding , have to fill it
        float Padding[2]= {0}; 
    };


    //for image widget
    vk::Sampler GetDefaultImageSampler();
    SVulkanDescriptorLayout GetImageWidgetDescriptorSetLayout();
    vk::PipelineLayout GetImageWidgetPipelineLayout();
    vk::Pipeline GetImageWidgetPipeline();

    struct SImageWidgetVertexPushConstant
    {
        static int GetSizeWithoutPadding()
        {
            return sizeof(SImageWidgetVertexPushConstant) - sizeof(float)*3;
        }

        float LeftTopUV[2];
        // RightBottomUV generally it is (1,1), if not (1,1), it is tile image.
        // LeftTopUV always is (0,0),
        float RightBottomUV[2];  
        float TintColor[4]={1,1,1,1};
        float ZOrder;
    };
    
    // SVulkanDescriptorLayout GetTextWidgetDescriptorSetLayout();
    // vk::PipelineLayout GetTextWidgetPipelineLayout();
    // vk::Pipeline GetTextWidgetPipeline();
    // vk::Sampler GetTextImageSampler();


    CSceneGlobal* GetSceneGlobal()
    {
        return &SceneGlobal;
    }

    struct SDefferShadingVertexModelUniform
    {
        SMatrix4x4 ModelMatrix;

        SDefferShadingVertexModelUniform& operator=(const SDefferShadingVertexModelUniform& Other)
        {
            ModelMatrix=Other.ModelMatrix;
            return *this;
        }

        bool operator==(const SDefferShadingVertexModelUniform& Other) const
        {
            return ModelMatrix==Other.ModelMatrix;
        }

        bool operator!=(const SDefferShadingVertexModelUniform& Other) const
        {
            return !(*this==Other);
        }


    };

    TVector<vk::VertexInputAttributeDescription> GetMeshVertexInputAttributeDescriptions();
    TVector<vk::VertexInputBindingDescription> GetMeshVertexInputBindingDescriptions();

    CMemoryPools* GetMemoryPools()
    {
        return &MemoryPools;
    }

    SVulkanRecycleObjects* GetRecycleObjects()
    {
        return &RecycleObjects;
    }
private:
    CMemoryPools MemoryPools;
    vk::CommandPool RenderThreadCommandPool;
    CVulkanAutoResetDescriptorPools AutoResetDescriptorPool[2];

    SVulkanRecycleObjects RecycleObjects;

    //完全由渲染线程管理的窗口对象
    //TVector<std::shared_ptr<CVulkanWindow>> VulkanWindows;
    TMap<uint64_t /*Window ID*/,std::shared_ptr<CVulkanWindow>> VulkanWindowMap;

    std::thread* RenderThread=nullptr;
    std::atomic<bool> RenderThreadInitialized=false;
    std::atomic<bool> VulkanThreadRunning=false;
    void VulkanThreadRun();
    void DrawWidgets(CAllPreRenderData*);



    vk::RenderPass WidgetRenderPass;
    void CreateWidgetRenderPass();

    SRectWidgetPipeline RectWidgetPipeline;

    vk::Fence ConvertImageLayoutFence;
    vk::CommandBuffer ConvertImageLayoutCommandBuffer;
    void CreateConvertImageLayoutFence();
    void CreateConvertImageLayoutCommandBuffer();

    vk::Fence CopyImageFence;
    vk::CommandBuffer CopyImageCommandBuffer;
    void CreateCopyImageFence();
    void CreateCopyImageCommandBuffer();

    SVulkanDescriptorLayout ImageWidgetDescriptorSetLayout;
    vk::PipelineLayout ImageWidgetPipelineLayout;
    vk::Pipeline ImageWidgetPipeline;
    vk::Sampler DefaultImageSampler;
    void CreateImageWidgetThings();

    // SVulkanDescriptorLayout TextWidgetDescriptorSetLayout;
    // vk::PipelineLayout TextWidgetPipelineLayout;
    // vk::Pipeline TextWidgetPipeline;
    // vk::Sampler TextImageSampler;
    // void CreateTextWidgetThings();

    SWidgetPipelines CanvasWidgetPipeline;
    STextWidgetPipeline TextWidgetPipeline;

    SForegroundMeshPipeline ForegroundMeshPipeline;

    CSceneGlobal SceneGlobal;

public:
    std::shared_ptr<CVulkanTexture2D> ToVulkanTexture2D(TThreadSafeSharedPtr<CTexture2DPixels> InTexture2D);
    std::shared_ptr<CVulkanTextureCube> ToVulkanTextureCube(TThreadSafeSharedPtr<CWriteInTextureCube> InTextureCube);
    std::shared_ptr<CVulkanMesh> ToVulkanMesh(TThreadSafeSharedPtr<CWriteInMesh> InMesh);
    std::shared_ptr<CVulkanShader> ToVulkanShader(TThreadSafeSharedPtr<CWriteInShader> InShader);
    std::shared_ptr<CVulkanMaterial> ToVulkanMaterial(std::shared_ptr<CPreRenderMaterial> InMaterial);

protected:
    //所有资源的处理器
    std::shared_ptr<CVulkanTexture2Ds> Texture2DMangers;
    std::shared_ptr<CVulkanTextureCubes> TextureCubeMangers;
    std::shared_ptr<CVulkanMeshs> MeshMangers;
    std::shared_ptr<CVulkanShaders> ShadersMangers;
    std::shared_ptr<CVulkanMaterials> MaterialsMangers[2];
    
};